#include "location_module.h"

#include "WLcJSON.h"
#include "wl_http_client.h"
#include "protocol_client.h"
#include "repo.h"
#include "session_module.h"
#include "weilapb.h"
#include "weilapb_defines.h"
#include "wlsdk_define.h"

#ifdef __cplusplus
extern "C" {
#endif

typedef enum {
    YBY_LOCATION_SEGMENT_TIMESTAMP_GPSSTATUS = 0,
    YBY_LOCATION_SEGMENT_GPS_PARAMS          = 1,
    YBY_LOCATION_SEGMENT_CDMA_PARAMS         = 2,
    YBY_LOCATION_SEGMENT_BTS_PARAMS          = 3,
    YBY_LOCATION_SEGMENT_WIFI_PARAMS         = 4
} yby_location_segment;

typedef enum { YBY_SEGMENT_TIMESTAMP = 0, YBY_SEGMENT_GPSSTATUS = 1 } yby_segment_timestamp_gpsstatus;

typedef enum {
    YBY_SEGMENT_GPS_LATITUDE        = 0,
    YBY_SEGMENT_GPS_LATITUDE_LABEL  = 1,
    YBY_SEGMENT_GPS_LONGITUDE       = 2,
    YBY_SEGMENT_GPS_LONGITUDE_LABEL = 3,
    YBY_SEGMENT_GPS_SPEED           = 4,
    YBY_SEGMENT_GPS_DIRECTION       = 5,
    YBY_SEGMENT_GPS_ALTITUDE        = 6,
    YBY_SEGMENT_GPS_SATELLITE_COUNT = 7,
    YBY_SEGMENT_GPS_SIGNAL          = 8
} yby_segment_gps_param;

typedef enum {
    YBY_SEGMENT_CDMA_STATUS = 0,
    YBY_SEGMENT_CDMA_SID    = 1,
    YBY_SEGMENT_CDMA_NID    = 2,
    YBY_SEGMENT_CDMA_BID    = 3,
    YBY_SEGMENT_CDMA_LON    = 4,
    YBY_SEGMENT_CDMA_LAT    = 5,
    YBY_SEGMENT_CDMA_SIGNAL = 6
} yby_segment_cdma_param;

typedef enum {
    YBY_SEGMENT_BTS_COUNT         = 0,
    YBY_SEGMENT_BTS_CONNECT_COUNT = 1,
    YBY_SEGMENT_BTS_MCC           = 2,
    YBY_SEGMENT_BTS_MNC           = 3,
    YBY_SEGMENT_BTS_LAC           = 4,
    YBY_SEGMENT_BTS_CELLID        = 5,
    YBY_SEGMENT_BTS_SIGNAL        = 6
} yby_segment_bts_param;

typedef enum {
    YBY_SEGMENT_WIFI_COUNT  = 0,
    YBY_SEGMENT_WIFI_NAME   = 1,
    YBY_SEGMENT_WIFI_MAC    = 2,
    YBY_SEGMENT_WIFI_SIGNAL = 3
} yby_segment_wifi_param;

#define LOCATION_REPORT_TIMER_INTERVAL 2000  // 共享定时器
#define LOCATION_REPORT_INTERVAL_LIMIT 5000  // 位置间隔不低于5秒
#define LOCATION_REPORT_STILL_TIME_LIMIT 60  // 静止位置上报间隔不低于60秒
#define LOCATION_STILL_DISTANCE 15           // 静止(移动距离小于15米)
#define LOCATION_GPS_PRORITY_TIME 60         // GPS优先有效时长(单位秒)
#define LOCATION_VALID_TIME 60               // 位置有效时长 60秒

#define LOCATION_SOS_TIMER_INTERVAL 30000  // SOS重试间隔 30s
#define LOCATION_SOS_RETRY_MAX_TIMES 10    // sos最多重试10次

typedef struct {
    wl_uint64 sessionid;
    wl_uint32 sessiontype;
    wl_int32  lifeleft;    // 生命期 小于等于0释放
    wl_int32  reportleft;  // 上报生命期 (小于等于0)重置
    wl_int32  frequency;   // 上报间隔
} wl_location_share;

typedef enum {
    LOCATION_HTTP_REPORT_LOCATION       = 0,
    LOCATION_HTTP_REPORT_TRACK          = 1,
    LOCATION_HTTP_REPORT_PHONE_SOS      = 2,
    LOCATION_HTTP_REPORT_WEILA_SOS      = 3,
    LOCATION_HTTP_GET_LBS_LOCATION      = 4,
    LOCATION_HTTP_REPORT_GEOFENCE_ALARM = 5
} wl_location_http_type;

typedef struct {
    wlsdk_message_callback callback;
    wlsdk_location         latest_gps;                  // 一分钟内GPS比LBS优先
    wlsdk_location         latest_location;             // 最新位置
    wlsdk_location         latest_share_location;       // 最新共享位置
    wlsdk_location         latest_track_location;       // 最新轨迹位置
    wl_uint32              sos_sn;                      // SOS 序号
    wl_int16               sos_phone_left_retry_times;  // 电话剩余SOS重试次数 小于等于0不再发送SOS
    wl_int16               sos_weila_left_retry_times;  // 微喇剩余SOS重试次数 小于等于0不再发送SOS
    wl_list                share_list;
    wl_uint32              track_timer_interval;  // 轨迹定时器间隔
    WL_TIMER               share_timer;           // 实时位置共享定时器
    WL_TIMER               track_timer;           // 轨迹上报定时器
    WL_TIMER               sos_timer;             // SOS重试定时器
    wl_bool                is_share_report;       // 是否上报实时位置分享
    wl_bool                is_track_report;       // 是否上报轨迹
    wl_bool                is_sos_report;         // 是否上报sos
} wl_location_global;

static wl_location_global s_location_global;

static void process_set_yby_location(void* data, wl_int32 datalen);
static void process_set_location(void* data, wl_int32 datalen);
static void process_set_lbs(void* data, wl_int32 datalen);
static wl_int32
    location_protocol_client_request_callback(PBWeilaMsg* message, void* content, wl_uint32 contentlen, void* context);
static wl_int32 location_protocol_client_listen_callback(PBWeilaMsg* message, void* context);
static void process_report_location_message(PBWeilaMsg* message, void* content, wl_uint32 contentlen, void* context);
static void
    process_report_track_location_message(PBWeilaMsg* message, void* content, wl_uint32 contentlen, void* context);
static void
    process_report_business_location_message(PBWeilaMsg* message, void* content, wl_uint32 contentlen, void* context);
static wl_int32 location_message_callback(wl_int32 msgid, void* data, wl_uint32 datalen);

static void location_sdk_message_callback(wl_uint32 msgid, wl_int32 resultcode, void* msgdata, wl_uint32 datalen) {
    if (s_location_global.callback) {
        WLLOGD("msgid %d", msgid);
        s_location_global.callback(msgid, resultcode, msgdata, datalen);
    }
}

static void location_http_client_callback(wl_uint32 resultcode,
                                          wl_char*  content,
                                          wl_uint32 content_len,
                                          wl_uint32 content_type,
                                          void*     context) {
    WLLOGD("result %d context:%d", resultcode, (wl_int32)context);
    if (WLSDK_RESULT_SUCCESS == resultcode) {
        if (content && HTTP_CLIENT_CONTENT_JSON == content_type) {
            WLcJSON* result_obj = WLcJSON_Parse(content);
            if (result_obj) {
                WLcJSON* errcode_obj = WLcJSON_GetObjectItem(result_obj, "errcode");
                if (errcode_obj) {
                    resultcode = (wl_int32)WLcJSON_GetNumberValue(errcode_obj);
                    WLLOGD("result %d", resultcode);
                    if (WLSDK_RESULT_SUCCESS == resultcode) {
                        // SOS成功响应
                        if (LOCATION_HTTP_REPORT_PHONE_SOS == (wl_int32)context) {
                            s_location_global.sos_phone_left_retry_times = 0;
                            location_sdk_message_callback(
                                WLSDK_MSG_SOS_PHONE_ALARM_RESPONSE, WLSDK_RESULT_SUCCESS, wl_null, 0);
                        } else if (LOCATION_HTTP_REPORT_WEILA_SOS == (wl_int32)context) {
                            s_location_global.sos_weila_left_retry_times = 0;
                            location_sdk_message_callback(
                                WLSDK_MSG_SOS_WEILA_ALARM_RESPONSE, WLSDK_RESULT_SUCCESS, wl_null, 0);
                        } else if (LOCATION_HTTP_GET_LBS_LOCATION == (wl_int32)context) {
                            wlsdk_location location;
                            WLcJSON*       value_obj;
                            WLcJSON*       data_obj = WLcJSON_GetObjectItem(result_obj, "data");
                            WLLOGW("content is %s", content);
                            memset(&location, 0, sizeof(wlsdk_location));
                            value_obj          = WLcJSON_GetObjectItem(data_obj, "latitude");
                            location.latitude  = WLcJSON_GetNumberValue(value_obj);
                            value_obj          = WLcJSON_GetObjectItem(data_obj, "longitude");
                            location.longitude = WLcJSON_GetNumberValue(value_obj);
                            location.type      = 1;  // gcj02
                            location.timestamp = repo_get_server_timestamp();
                            location_sdk_message_callback(
                                WLSDK_MSG_LOCATION_RESPONSE_LBS, WLSDK_RESULT_SUCCESS, &location, sizeof(location));

                            // 处理lbs位置
                            {
                                location_set_location msgdata;
                                memset(&msgdata, 0, sizeof(location_set_location));
                                memcpy(&msgdata.location, &location, sizeof(wlsdk_location));
                                msgdata.isgps = wl_false;
                                process_set_location(&msgdata, sizeof(msgdata));
                            }
                        } else if (LOCATION_HTTP_REPORT_GEOFENCE_ALARM == (wl_int32)context) {
                            WLcJSON*  value_obj;
                            WLcJSON*  data_obj          = WLcJSON_GetObjectItem(result_obj, "data");
                            wl_uint64 geofenceid        = 0;
                            wl_uint32 transition_status = 0;
                            WLLOGW("content is %s", content);
                            value_obj = WLcJSON_GetObjectItem(data_obj, "geofence_id");
                            if (value_obj) {
                                geofenceid = WLcJSON_GetNumberValue(value_obj);
                            }
                            value_obj = WLcJSON_GetObjectItem(data_obj, "transition_status");
                            if (value_obj) {
                                transition_status = WLcJSON_GetNumberValue(value_obj);
                            }
                            repo_set_extension_config_geofence_transition_status(geofenceid, transition_status);
                        }
                    }
                } else {
                    resultcode = WLSDK_RESULT_DATA_INVALID;
                }
                WLcJSON_Delete(result_obj);
            } else {
                resultcode = WLSDK_RESULT_DATA_INVALID;
            }

            WLLOGD("resultcode:%d context:%d", resultcode, (wl_int32)context);
            if (WLSDK_RESULT_SUCCESS != resultcode) {
                // SOS失败响应
                if (LOCATION_HTTP_REPORT_PHONE_SOS == (wl_int32)context) {
                    if (0 >= s_location_global.sos_phone_left_retry_times) {
                        location_sdk_message_callback(WLSDK_MSG_SOS_PHONE_ALARM_RESPONSE, resultcode, wl_null, 0);
                    }
                } else if (LOCATION_HTTP_REPORT_WEILA_SOS == (wl_int32)context) {
                    if (0 >= s_location_global.sos_phone_left_retry_times) {
                        location_sdk_message_callback(WLSDK_MSG_SOS_WEILA_ALARM_RESPONSE, resultcode, wl_null, 0);
                    }
                } else if (LOCATION_HTTP_GET_LBS_LOCATION == (wl_int32)context) {
                    location_sdk_message_callback(WLSDK_MSG_LOCATION_RESPONSE_LBS, resultcode, wl_null, 0);
                }
            }
        }
    } else {
        if (LOCATION_HTTP_GET_LBS_LOCATION == (wl_int32)context) {
            location_sdk_message_callback(WLSDK_MSG_LOCATION_RESPONSE_LBS, resultcode, wl_null, 0);
        }
    }
}

wl_int32 location_delete_share(wl_uint64 sessionid, wl_uint32 sessiontype) {
    wl_list_iterator   iterator;
    wl_location_share* shareinfo = (wl_location_share*)wl_list_get_first(&s_location_global.share_list, &iterator);
    while (!wl_list_iterator_is_end(&iterator)) {
        if (shareinfo->sessionid == sessionid && shareinfo->sessiontype == sessiontype) {
            wl_free(shareinfo);
            wl_list_iterator_remove(&iterator);
            break;
        }
        shareinfo = (wl_location_share*)wl_list_get_next(&iterator);
    }
    return 0;
}

wl_int32 location_add_share(wl_location_share* share) {
    wl_location_share* shareinfo = (wl_location_share*)wl_malloc(sizeof(wl_location_share));
    if (wl_null == shareinfo) {
        WLLOGW("malloc fail");
        return -1;
    }
    // 先删除share
    location_delete_share(share->sessionid, share->sessiontype);
    memcpy(shareinfo, share, sizeof(wl_location_share));
    if (-1 == wl_list_add(&s_location_global.share_list, shareinfo, -1)) {
        WLLOGW("wl_list_add fail");
        wl_free(shareinfo);
        return -1;
    }
    return 0;
}

wl_int32 location_get_share(wl_uint64 sessionid, wl_uint32 sessiontype, wl_location_share* share) {
    wl_list_iterator   iterator;
    wl_location_share* shareinfo = (wl_location_share*)wl_list_get_first(&s_location_global.share_list, &iterator);
    while (!wl_list_iterator_is_end(&iterator)) {
        if (shareinfo->sessionid == sessionid && shareinfo->sessiontype == sessiontype) {
            memcpy(share, shareinfo, sizeof(wl_location_share));
            return 0;
        }
        shareinfo = (wl_location_share*)wl_list_get_next(&iterator);
    }
    return -1;
}

static wl_bool location_is_share(wl_uint64 sessionid, wl_uint32 sessiontype) {
    // 共享关闭
    if (!repo_get_extension_config_location_share()) {
        // 管理员单独查看
        if (WLSDK_SESSION_TYPE_SINGLE == sessiontype && repo_get_managerid() == sessionid) {
            return wl_true;
        }
        return wl_false;
    }
    return wl_true;
}

static void location_share_timer_callback(WL_TIMER timer, void* context) {
    wl_list_iterator   iterator;
    wl_location_share* shareinfo    = (wl_location_share*)wl_list_get_first(&s_location_global.share_list, &iterator);
    wl_bool            isneedreport = wl_false;  // 是否需要上报
    while (!wl_list_iterator_is_end(&iterator)) {
        shareinfo->lifeleft -= LOCATION_REPORT_TIMER_INTERVAL;
        shareinfo->reportleft -= LOCATION_REPORT_TIMER_INTERVAL;
        // 过期
        if (shareinfo->lifeleft < 0) {
            wl_free(shareinfo);
            shareinfo = wl_list_iterator_remove(&iterator);
            continue;
        }
        if (shareinfo->reportleft <= 0) {
            if (location_is_share(shareinfo->sessionid, shareinfo->sessiontype)) {
                isneedreport = wl_true;
            }
            shareinfo->reportleft = shareinfo->frequency;
        }
        shareinfo = (wl_location_share*)wl_list_get_next(&iterator);
    }

    if (isneedreport) {
        WLLOGD("request realtime location");
        s_location_global.is_share_report = wl_true;
        location_sdk_message_callback(WLSDK_MSG_LOCATION_REQUEST_REALTIME, WLSDK_RESULT_SUCCESS, wl_null, 0);
    }
    // 没有位置共享停止定时器
    if (wl_list_size(&s_location_global.share_list) <= 0) {
        if (WL_INVALID_TIMER != s_location_global.share_timer) {
            wl_timer_stop(s_location_global.share_timer);
            s_location_global.share_timer = WL_INVALID_TIMER;
        }
    }
}

static void location_start_share(wl_uint64 sessionid, wl_uint16 sessiontype, wl_uint16 duration, wl_uint16 frequency) {
    wl_location_share share;
    // 新请求立即上报
    if (0 != location_get_share(sessionid, sessiontype, &share)) {
        WLLOGD("request realtime location");
        if (location_is_share(sessionid, sessiontype)) {
            s_location_global.is_share_report = wl_true;
            // SOS请求只在OPEN时调用,后续重传使用实时位置请求
            location_sdk_message_callback(WLSDK_MSG_LOCATION_REQUEST_REALTIME, WLSDK_RESULT_SUCCESS, wl_null, 0);
        }
        share.reportleft = LOCATION_REPORT_INTERVAL_LIMIT;
    }
    share.sessionid   = sessionid;
    share.sessiontype = sessiontype;
    share.lifeleft    = (duration * 1000 + LOCATION_REPORT_TIMER_INTERVAL);
    share.frequency =
        frequency * 1000 > LOCATION_REPORT_INTERVAL_LIMIT ? (frequency * 1000) : LOCATION_REPORT_INTERVAL_LIMIT;
    location_add_share(&share);

    if (wl_list_size(&s_location_global.share_list) > 0) {
        if (WL_INVALID_TIMER == s_location_global.share_timer) {
            // 超时定时器
            s_location_global.share_timer = wl_timer_start(LOCATION_REPORT_TIMER_INTERVAL,
                                                           REPETITIVE,
                                                           location_share_timer_callback,
                                                           wl_null,
                                                           WL_TASK_APP,
                                                           "loc_share");
            WLLOGI("timer %p", s_location_global.share_timer);
            if (WL_INVALID_TIMER == s_location_global.share_timer) {
                WLLOGW("wl_timer_start fail");
                return;
            }
        }
    }
}

static void location_stop_share(wl_uint64 sessionid, wl_uint16 sessiontype) {
    location_delete_share(sessionid, sessiontype);
}

static void location_track_timer_callback(WL_TIMER timer, void* context) {
    // 在线
    if (WLSDK_LOGIN_ONLINE == repo_get_login_state() || WLSDK_LOGIN_SYNCING == repo_get_login_state()) {
        // 0 不上报轨迹
        if (0 != repo_get_extension_config_location_interval()) {
            s_location_global.is_track_report = wl_true;
            location_sdk_message_callback(WLSDK_MSG_LOCATION_REQUEST_TRACK, WLSDK_RESULT_SUCCESS, wl_null, 0);
        }
    }
    // 轨迹关闭
    if (repo_get_extension_config_location_interval() == 0) {
        if (WL_INVALID_TIMER != s_location_global.track_timer) {
            wl_timer_stop(s_location_global.track_timer);
            s_location_global.track_timer_interval = 0;
            s_location_global.track_timer          = WL_INVALID_TIMER;
        }
    }
}

static void location_track_config_refresh(void) {
    wl_uint32 interval = repo_get_extension_config_location_interval();
    // 轨迹上报间隔变更
    if (interval != s_location_global.track_timer_interval) {
        // 创建轨迹定时器
        if (interval > 0) {
            if (WL_INVALID_TIMER != s_location_global.track_timer) {
                wl_timer_stop(s_location_global.track_timer);
                s_location_global.track_timer_interval = 0;
                s_location_global.track_timer          = WL_INVALID_TIMER;
            }
            s_location_global.track_timer = wl_timer_start(
                interval * 1000, REPETITIVE, location_track_timer_callback, wl_null, WL_TASK_APP, "loc_track");
            WLLOGI("timer %p", s_location_global.track_timer);
            if (WL_INVALID_TIMER == s_location_global.track_timer) {
                s_location_global.track_timer_interval = 0;
                WLLOGW("wl_timer_start fail");
                return;
            }
            s_location_global.track_timer_interval = interval;
            s_location_global.is_track_report      = wl_true;
            location_sdk_message_callback(WLSDK_MSG_LOCATION_REQUEST_TRACK, WLSDK_RESULT_SUCCESS, wl_null, 0);
        }
    }
    // 轨迹关闭
    if (interval == 0) {
        if (WL_INVALID_TIMER != s_location_global.track_timer) {
            wl_timer_stop(s_location_global.track_timer);
            s_location_global.track_timer = WL_INVALID_TIMER;
        }
    }
}

static void location_sos_timer_callback(WL_TIMER timer, void* context) {
    if (0 != s_location_global.sos_sn
        && (0 < s_location_global.sos_phone_left_retry_times || 0 < s_location_global.sos_weila_left_retry_times)) {
        s_location_global.is_sos_report = wl_true;
        location_sdk_message_callback(WLSDK_MSG_LOCATION_REQUEST_REALTIME, WLSDK_RESULT_SUCCESS, wl_null, 0);
        return;
    }
    s_location_global.is_sos_report              = wl_false;
    s_location_global.sos_sn                     = 0;
    s_location_global.sos_phone_left_retry_times = 0;
    s_location_global.sos_weila_left_retry_times = 0;
    if (WL_INVALID_TIMER != s_location_global.sos_timer) {
        wl_timer_stop(s_location_global.sos_timer);
        s_location_global.sos_timer = WL_INVALID_TIMER;
    }
}

wl_int32 location_module_init(wlsdk_message_callback callback) {
    wl_int32  ret = 0;
    wl_module module;

    WLLOGI("location_module_init");
    memset(&module, 0, sizeof(wl_module));
    module.moduleid = WL_MODULE_LOCATION;
    module.taskid   = WL_TASK_APP;
    module.callback = location_message_callback;
    ret             = wl_core_register_module(&module);
    if (0 != ret) {
        WLLOGW("wl_core_register_module location fail");
        return ret;
    }
    s_location_global.share_timer                = WL_INVALID_TIMER;
    s_location_global.track_timer                = WL_INVALID_TIMER;
    s_location_global.sos_timer                  = WL_INVALID_TIMER;
    s_location_global.sos_sn                     = 0;
    s_location_global.sos_phone_left_retry_times = 0;
    s_location_global.sos_weila_left_retry_times = 0;
    s_location_global.callback                   = callback;
    s_location_global.track_timer_interval       = 0;
    s_location_global.is_share_report            = wl_false;
    s_location_global.is_track_report            = wl_false;
    s_location_global.is_sos_report              = wl_false;
    protocol_client_add_listen(
        WL_LOCATION_LOCATION_CONTROL_MSG, WEILA_MSG_IND, location_protocol_client_listen_callback, wl_null);
    return ret;
}

wl_int32 location_module_end(void) {
    WLLOGI("location_module_end");
    wl_core_unregister_module(WL_MODULE_LOCATION);
    s_location_global.callback = wl_null;
    if (WL_INVALID_TIMER != s_location_global.share_timer) {
        wl_timer_stop(s_location_global.share_timer);
        s_location_global.share_timer = WL_INVALID_TIMER;
    }
    if (WL_INVALID_TIMER != s_location_global.sos_timer) {
        wl_timer_stop(s_location_global.sos_timer);
        s_location_global.sos_timer = WL_INVALID_TIMER;
    }
    return 0;
}

static wl_char* location_create_location_content(wlsdk_location* location) {
    WLcJSON* root_obj = WLcJSON_CreateObject();
    WLcJSON* gps_obj  = wl_null;
    wl_char* content  = wl_null;
    if (wl_null == root_obj) {
        WLLOGW("WLcJSON_CreateObject fail");
        return wl_null;
    }
    gps_obj = WLcJSON_CreateObject();
    if (wl_null == gps_obj) {
        WLLOGW("WLcJSON_CreateObject fail");
        return wl_null;
    }
    repo_build_http_sign(root_obj);
    if (s_location_global.sos_sn) {
        WLcJSON_AddNumberToObject(root_obj, "sn", s_location_global.sos_sn);
    }
    WLcJSON_AddNumberToObject(gps_obj, "latitude", location->latitude);
    WLcJSON_AddNumberToObject(gps_obj, "longitude", location->longitude);
    WLcJSON_AddNumberToObject(gps_obj, "location_type", location->type);
    WLcJSON_AddItemToObject(root_obj, "gps", gps_obj);
    content = WLcJSON_PrintUnformatted(root_obj);

    WLcJSON_Delete(root_obj);
    return content;
}

static wl_char*
    location_create_geofence_alarm_content(wlsdk_location* location, wl_uint64 geofenceid, wl_uint32 transitionstatus) {
    WLcJSON* root_obj = WLcJSON_CreateObject();
    WLcJSON* gps_obj  = wl_null;
    wl_char* content  = wl_null;
    if (wl_null == root_obj) {
        WLLOGW("WLcJSON_CreateObject fail");
        return wl_null;
    }
    gps_obj = WLcJSON_CreateObject();
    if (wl_null == gps_obj) {
        WLLOGW("WLcJSON_CreateObject fail");
        return wl_null;
    }
    repo_build_http_sign(root_obj);
    if (s_location_global.sos_sn) {
        WLcJSON_AddNumberToObject(root_obj, "sn", s_location_global.sos_sn);
    }
    WLcJSON_AddNumberToObject(gps_obj, "latitude", location->latitude);
    WLcJSON_AddNumberToObject(gps_obj, "longitude", location->longitude);
    WLcJSON_AddNumberToObject(gps_obj, "location_type", location->type);
    WLcJSON_AddItemToObject(root_obj, "gps", gps_obj);
    WLcJSON_AddNumberToObject(root_obj, "geofence_id", geofenceid);
    WLcJSON_AddNumberToObject(root_obj, "transition_status", transitionstatus);
    content = WLcJSON_PrintUnformatted(root_obj);

    WLcJSON_Delete(root_obj);
    return content;
}

static wl_char* location_create_get_lbs_body(wlsdk_lbs* lbs) {
    WLcJSON*      root_obj = WLcJSON_CreateObject();
    WLcJSON*      lbs_obj  = wl_null;
    WLcJSON*      bts_obj  = wl_null;
    repo_hardware hardware;
    wl_char*      body  = wl_null;
    wl_int32      index = 0;
    if (wl_null == root_obj) {
        WLLOGW("WLcJSON_CreateObject fail");
        return wl_null;
    }
    lbs_obj = WLcJSON_CreateObject();
    if (wl_null == lbs_obj) {
        WLcJSON_Delete(root_obj);
        WLLOGW("WLcJSON_CreateObject fail");
        return wl_null;
    }
    bts_obj = WLcJSON_CreateObject();
    if (wl_null == bts_obj) {
        WLcJSON_Delete(root_obj);
        WLcJSON_Delete(lbs_obj);
        WLLOGW("WLcJSON_CreateObject fail");
        return wl_null;
    }
    repo_get_hardware(&hardware);

    if ('\0' != hardware.imei[0]) {
        WLcJSON_AddStringToObject(lbs_obj, "imei", hardware.imei);
    }
    if ('\0' != hardware.imsi[0]) {
        WLcJSON_AddStringToObject(lbs_obj, "imsi", hardware.imsi);
    }
    WLcJSON_AddNumberToObject(lbs_obj, "cdma", lbs->bts.iscdma);
    WLcJSON_AddNumberToObject(lbs_obj, "accesstype", 0);
    // bts
    if (!lbs->bts.iscdma) {
        WLcJSON_AddNumberToObject(bts_obj, "mcc", lbs->bts.bts.mobile.mcc);
        WLcJSON_AddNumberToObject(bts_obj, "mnc", lbs->bts.bts.mobile.mnc);
        WLcJSON_AddNumberToObject(bts_obj, "lac", lbs->bts.bts.mobile.lac);
        WLcJSON_AddNumberToObject(bts_obj, "cellid", lbs->bts.bts.mobile.cellid);
        WLcJSON_AddNumberToObject(bts_obj, "signal", lbs->bts.bts.mobile.signal);
    } else {
        WLcJSON_AddNumberToObject(bts_obj, "sid", lbs->bts.bts.cdma.sid);
        WLcJSON_AddNumberToObject(bts_obj, "nid", lbs->bts.bts.cdma.nid);
        WLcJSON_AddNumberToObject(bts_obj, "bid", lbs->bts.bts.cdma.bid);
        WLcJSON_AddNumberToObject(bts_obj, "signal", lbs->bts.bts.cdma.signal);
    }
    WLcJSON_AddItemToObject(lbs_obj, "bts", bts_obj);

    // nearbts
    if (lbs->nearbts_count) {
        WLcJSON* nearbtss_obj = WLcJSON_CreateArray();
        for (index = 0; index < lbs->nearbts_count && index < MAX_NEAR_BTS_NUM; index++) {
            WLcJSON* nearbts_obj = WLcJSON_CreateObject();
            WLcJSON_AddNumberToObject(nearbts_obj, "iscdma", lbs->nearbts[index].iscdma);
            // bts
            if (!lbs->nearbts[index].iscdma) {
                WLcJSON_AddNumberToObject(nearbts_obj, "mcc", lbs->nearbts[index].bts.mobile.mcc);
                WLcJSON_AddNumberToObject(nearbts_obj, "mnc", lbs->nearbts[index].bts.mobile.mnc);
                WLcJSON_AddNumberToObject(nearbts_obj, "lac", lbs->nearbts[index].bts.mobile.lac);
                WLcJSON_AddNumberToObject(nearbts_obj, "cellid", lbs->nearbts[index].bts.mobile.cellid);
                WLcJSON_AddNumberToObject(nearbts_obj, "signal", lbs->nearbts[index].bts.mobile.signal);
            } else {
                WLcJSON_AddNumberToObject(nearbts_obj, "sid", lbs->nearbts[index].bts.cdma.sid);
                WLcJSON_AddNumberToObject(nearbts_obj, "nid", lbs->nearbts[index].bts.cdma.nid);
                WLcJSON_AddNumberToObject(nearbts_obj, "bid", lbs->nearbts[index].bts.cdma.bid);
                WLcJSON_AddNumberToObject(nearbts_obj, "signal", lbs->nearbts[index].bts.cdma.signal);
            }
            WLcJSON_AddItemToArray(nearbtss_obj, nearbts_obj);
        }
        WLcJSON_AddItemToObject(lbs_obj, "nearbts", nearbtss_obj);
    }

    // nearwifi
    if (lbs->nearwifi_count) {
        WLcJSON* nearwifis_obj = WLcJSON_CreateArray();
        for (index = 0; index < lbs->nearwifi_count && index < MAX_NEAR_WIFI_NUM; index++) {
            WLcJSON* nearwifi_obj = WLcJSON_CreateObject();
            WLcJSON_AddStringToObject(nearwifi_obj, "mac", lbs->nearwifi[index].mac);
            WLcJSON_AddStringToObject(nearwifi_obj, "ssid", lbs->nearwifi[index].ssid);
            WLcJSON_AddNumberToObject(nearwifi_obj, "signal", lbs->nearwifi[index].signal);
            WLcJSON_AddItemToArray(nearwifis_obj, nearwifi_obj);
        }
        WLcJSON_AddItemToObject(lbs_obj, "macs", nearwifis_obj);
    }

    WLcJSON_AddItemToObject(root_obj, "lbs", lbs_obj);
    body = WLcJSON_PrintUnformatted(root_obj);

    WLcJSON_Delete(root_obj);
    return body;
}

static wl_char* location_create_lbs_content(wlsdk_lbs* lbs, wl_char* content) {
    WLcJSON*      root_obj = WLcJSON_CreateObject();
    WLcJSON*      lbs_obj  = wl_null;
    WLcJSON*      bts_obj  = wl_null;
    repo_hardware hardware;
    wl_char*      body  = wl_null;
    wl_int32      index = 0;
    if (wl_null == root_obj) {
        WLLOGW("WLcJSON_CreateObject fail");
        return wl_null;
    }
    lbs_obj = WLcJSON_CreateObject();
    if (wl_null == lbs_obj) {
        WLcJSON_Delete(root_obj);
        WLLOGW("WLcJSON_CreateObject fail");
        return wl_null;
    }
    bts_obj = WLcJSON_CreateObject();
    if (wl_null == bts_obj) {
        WLcJSON_Delete(root_obj);
        WLcJSON_Delete(lbs_obj);
        WLLOGW("WLcJSON_CreateObject fail");
        return wl_null;
    }
    repo_get_hardware(&hardware);

    repo_build_http_sign(root_obj);
    if (strlen(content)) {
        WLcJSON_AddStringToObject(lbs_obj, "content", content);
    }
    if (s_location_global.sos_sn) {
        WLcJSON_AddNumberToObject(root_obj, "sn", s_location_global.sos_sn);
    }

    if ('\0' != hardware.imei[0]) {
        WLcJSON_AddStringToObject(lbs_obj, "imei", hardware.imei);
    }
    if ('\0' != hardware.imsi[0]) {
        WLcJSON_AddStringToObject(lbs_obj, "imsi", hardware.imsi);
    }
    WLcJSON_AddNumberToObject(lbs_obj, "cdma", lbs->bts.iscdma);
    WLcJSON_AddNumberToObject(lbs_obj, "accesstype", 0);
    // bts
    if (!lbs->bts.iscdma) {
        WLcJSON_AddNumberToObject(bts_obj, "mcc", lbs->bts.bts.mobile.mcc);
        WLcJSON_AddNumberToObject(bts_obj, "mnc", lbs->bts.bts.mobile.mnc);
        WLcJSON_AddNumberToObject(bts_obj, "lac", lbs->bts.bts.mobile.lac);
        WLcJSON_AddNumberToObject(bts_obj, "cellid", lbs->bts.bts.mobile.cellid);
        WLcJSON_AddNumberToObject(bts_obj, "signal", lbs->bts.bts.mobile.signal);
    } else {
        WLcJSON_AddNumberToObject(bts_obj, "sid", lbs->bts.bts.cdma.sid);
        WLcJSON_AddNumberToObject(bts_obj, "nid", lbs->bts.bts.cdma.nid);
        WLcJSON_AddNumberToObject(bts_obj, "bid", lbs->bts.bts.cdma.bid);
        WLcJSON_AddNumberToObject(bts_obj, "signal", lbs->bts.bts.cdma.signal);
    }
    WLcJSON_AddItemToObject(lbs_obj, "bts", bts_obj);

    // nearbts
    if (lbs->nearbts_count) {
        WLcJSON* nearbtss_obj = WLcJSON_CreateArray();
        for (index = 0; index < lbs->nearbts_count && index < MAX_NEAR_BTS_NUM; index++) {
            WLcJSON* nearbts_obj = WLcJSON_CreateObject();
            WLcJSON_AddNumberToObject(nearbts_obj, "iscdma", lbs->nearbts[index].iscdma);
            // bts
            if (!lbs->nearbts[index].iscdma) {
                WLcJSON_AddNumberToObject(nearbts_obj, "mcc", lbs->nearbts[index].bts.mobile.mcc);
                WLcJSON_AddNumberToObject(nearbts_obj, "mnc", lbs->nearbts[index].bts.mobile.mnc);
                WLcJSON_AddNumberToObject(nearbts_obj, "lac", lbs->nearbts[index].bts.mobile.lac);
                WLcJSON_AddNumberToObject(nearbts_obj, "cellid", lbs->nearbts[index].bts.mobile.cellid);
                WLcJSON_AddNumberToObject(nearbts_obj, "signal", lbs->nearbts[index].bts.mobile.signal);
            } else {
                WLcJSON_AddNumberToObject(nearbts_obj, "sid", lbs->nearbts[index].bts.cdma.sid);
                WLcJSON_AddNumberToObject(nearbts_obj, "nid", lbs->nearbts[index].bts.cdma.nid);
                WLcJSON_AddNumberToObject(nearbts_obj, "bid", lbs->nearbts[index].bts.cdma.bid);
                WLcJSON_AddNumberToObject(nearbts_obj, "signal", lbs->nearbts[index].bts.cdma.signal);
            }
            WLcJSON_AddItemToArray(nearbtss_obj, nearbts_obj);
        }
        WLcJSON_AddItemToObject(lbs_obj, "nearbts", nearbtss_obj);
    }

    // nearwifi
    if (lbs->nearwifi_count) {
        WLcJSON* nearwifis_obj = WLcJSON_CreateArray();
        for (index = 0; index < lbs->nearwifi_count && index < MAX_NEAR_WIFI_NUM; index++) {
            WLcJSON* nearwifi_obj = WLcJSON_CreateObject();
            WLcJSON_AddStringToObject(nearwifi_obj, "mac", lbs->nearwifi[index].mac);
            WLcJSON_AddStringToObject(nearwifi_obj, "ssid", lbs->nearwifi[index].ssid);
            WLcJSON_AddNumberToObject(nearwifi_obj, "signal", lbs->nearwifi[index].signal);
            WLcJSON_AddItemToArray(nearwifis_obj, nearwifi_obj);
        }
        WLcJSON_AddItemToObject(lbs_obj, "macs", nearwifis_obj);
    }
    // 添加共享会话
    if (wl_list_size(&s_location_global.share_list) > 0) {
        wl_list_iterator   iterator;
        wl_location_share* shareinfo = (wl_location_share*)wl_list_get_first(&s_location_global.share_list, &iterator);
        WLcJSON*           sessions_obj = WLcJSON_CreateArray();
        while (!wl_list_iterator_is_end(&iterator)) {
            if (location_is_share(shareinfo->sessionid, shareinfo->sessiontype)) {
                WLcJSON* session_obj = WLcJSON_CreateObject();
                WLcJSON_AddNumberToObject(session_obj, "session_id", shareinfo->sessionid);
                WLcJSON_AddNumberToObject(session_obj, "session_type", shareinfo->sessiontype);
                WLcJSON_AddItemToArray(sessions_obj, session_obj);
            }
            shareinfo = (wl_location_share*)wl_list_get_next(&iterator);
        }
        WLcJSON_AddItemToObject(lbs_obj, "sessions", sessions_obj);
    }

    WLcJSON_AddItemToObject(root_obj, "lbs", lbs_obj);
    body = WLcJSON_PrintUnformatted(root_obj);

    WLcJSON_Delete(root_obj);
    return body;
}

static void location_http_request(wl_int32 method_type, wl_char* content) {
    wl_char     server_url[MAX_URL_LEN];
    wl_char     token[MAX_TOKEN_LEN + 1];
    wl_char*    method = "report-location";
    repo_server web_server;
    repo_get_token(token);
    repo_get_web_server(&web_server);
    switch (method_type) {
        case LOCATION_HTTP_REPORT_TRACK:
            method = "report-track";
            break;
        case LOCATION_HTTP_REPORT_PHONE_SOS:
            method = "report-phone-sos";
            break;
        case LOCATION_HTTP_REPORT_WEILA_SOS:
            method = "report-weila-sos";
            break;
        case LOCATION_HTTP_GET_LBS_LOCATION:
            method = "get-lbs-location";
            break;
        case LOCATION_HTTP_REPORT_GEOFENCE_ALARM:
            method = "report-geofence-alarm";
            break;
        default:
            break;
    }
    sprintf(server_url,
            "http://%s:%d/v1/users/location/%s?access-token=%s",
            web_server.host,
            web_server.port,
            method,
            token);
    WLLOGD("server_url %s", server_url);
    if (0
        != wl_http_client_post(server_url,
                            HTTP_CLIENT_REQUEST_TIMEOUT_15,
                            content,
                            strlen(content),
                            HTTP_CLIENT_CONTENT_JSON,
                            location_http_client_callback,
                            (void*)method_type)) {
        WLLOGW("wl_http_client_post %s error", server_url);
    }
}

static void location_report_geofence_alarm(wlsdk_location* location, wl_uint64 geofenceid, wl_uint32 transitionstatus) {
    wl_char* content = location_create_geofence_alarm_content(location, geofenceid, transitionstatus);
    if (content) {
        location_http_request(LOCATION_HTTP_REPORT_GEOFENCE_ALARM, content);
        WLcJSON_free(content);
    }
}

static void location_report_gps_location(wlsdk_location* location) {
    wl_char* content = location_create_location_content(location);
    if (content) {
        location_http_request(LOCATION_HTTP_REPORT_TRACK, content);
        WLcJSON_free(content);
    }
}

static void location_get_lbs_location(wlsdk_lbs* lbs) {
    wl_char* body = location_create_get_lbs_body(lbs);
    WLLOGD("location_get_lbs_location");
    if (body) {
        location_http_request(LOCATION_HTTP_GET_LBS_LOCATION, body);
        WLcJSON_free(body);
    }
}

static void location_report_lbs_location(wlsdk_lbs* lbs, wl_char* content) {
    wl_char* body = location_create_lbs_content(lbs, content);
    if (body) {
        location_http_request(LOCATION_HTTP_REPORT_LOCATION, body);
        WLcJSON_free(body);
    }
}

static void location_report_track_gps_location(wlsdk_location* location) {
    if (0 != repo_get_extension_config_location_interval()) {
        location_report_gps_location(location);
    }
}

static void location_report_track_lbs_location(wlsdk_lbs* lbs, wl_char* content) {
    if (0 != repo_get_extension_config_location_interval()) {
        // 获取LBS位置信息
        location_get_lbs_location(lbs);
        // wl_char* body = location_create_lbs_content(lbs, content);
        // if (body) {
        //     location_http_request(LOCATION_HTTP_REPORT_TRACK, body);
        //     WLcJSON_free(body);
        // }
    }
}

static void location_report_sos_gps_location(wlsdk_location* location) {
    wl_char* content = location_create_location_content(location);
    if (content) {
        if (0 < s_location_global.sos_phone_left_retry_times) {
            location_http_request(LOCATION_HTTP_REPORT_PHONE_SOS, content);
            s_location_global.sos_phone_left_retry_times--;
        }
        if (0 < s_location_global.sos_weila_left_retry_times) {
            location_http_request(LOCATION_HTTP_REPORT_WEILA_SOS, content);
            s_location_global.sos_weila_left_retry_times--;
        }
        WLcJSON_free(content);
    }
}

static void location_report_sos_lbs_location(wlsdk_lbs* lbs, wl_char* content) {
    wl_char* body = location_create_lbs_content(lbs, content);
    if (body) {
        if (0 < s_location_global.sos_phone_left_retry_times) {
            location_http_request(LOCATION_HTTP_REPORT_PHONE_SOS, body);
            s_location_global.sos_phone_left_retry_times--;
        }
        if (0 < s_location_global.sos_weila_left_retry_times) {
            location_http_request(LOCATION_HTTP_REPORT_WEILA_SOS, body);
            s_location_global.sos_weila_left_retry_times--;
        }
        WLcJSON_free(body);
    }
}

static void location_report_location(wlsdk_location* location) {
    PBEncResult    enc_result;
    PBLocationInfo location_info;
    wl_int32       resultcode;
    PBSessionInfo* sessions      = wl_null;
    wl_int32       session_count = wl_list_size(&s_location_global.share_list);
    wl_int32       share_count   = 0;  // 实际共享会话数
    wl_double      distance      = wl_getdistance(location->longitude,
                                        location->latitude,
                                        s_location_global.latest_share_location.longitude,
                                        s_location_global.latest_share_location.latitude);  // 计算距离
    if (repo_get_login_state() != WLSDK_LOGIN_ONLINE) {
        WLLOGD("user offline");
        return;
    }
    if (distance < LOCATION_STILL_DISTANCE) {
        // 限制静止状态上报频率
        if (location->timestamp
            < s_location_global.latest_share_location.timestamp + LOCATION_REPORT_STILL_TIME_LIMIT) {
            WLLOGW("distance %f time %u latest timestamp %u",
                   distance,
                   location->timestamp,
                   s_location_global.latest_share_location.timestamp);
            return;
        }
    }

    location_info.userId       = repo_get_userid();
    location_info.clientType   = repo_get_extension_config_client_type();
    location_info.latitude     = location->latitude;
    location_info.longitude    = location->longitude;
    location_info.locationType = 0;
    location_info.speed        = location->speed;
    location_info.altitude     = location->altitude;
    location_info.radius       = location->radius;
    location_info.direction    = location->direction;
    location_info.timestamp    = location->timestamp;

    if (session_count) {
        sessions = (PBSessionInfo*)wl_malloc(session_count * sizeof(PBSessionInfo));
        if (sessions) {
            wl_list_iterator   iterator;
            wl_location_share* shareinfo =
                (wl_location_share*)wl_list_get_first(&s_location_global.share_list, &iterator);
            while (!wl_list_iterator_is_end(&iterator)) {
                if (location_is_share(shareinfo->sessionid, shareinfo->sessiontype)) {
                    sessions[share_count].sessionId   = shareinfo->sessionid;
                    sessions[share_count].sessionType = shareinfo->sessiontype;
                    share_count++;
                }
                shareinfo = (wl_location_share*)wl_list_get_next(&iterator);
            }
        } else {
            WLLOGI("wl_malloc fail");
            return;
        }
    }

    // 没有位置共享
    if (share_count <= 0) {
        return;
    }

    memcpy(&s_location_global.latest_share_location, location, sizeof(wlsdk_location));
    enc_result = weilapb_buildReportLocationReq(sessions, share_count, &location_info, wl_null);
    resultcode = protocol_client_send_request(&enc_result,
                                              wl_null,
                                              0,
                                              PROTOCOL_CLIENT_REQUEST_TIMEOUT_15,
                                              location_protocol_client_request_callback,
                                              wl_null);
    wl_free(sessions);
    sessions = wl_null;
    if (0 > resultcode) {
        WLLOGW("protocol_client_send_request error");
    }
}

static void location_report_track_location(wlsdk_location* location) {
    PBEncResult    enc_result;
    PBLocationInfo location_info;
    wl_int32       resultcode;
    wl_double      distance = wl_getdistance(location->longitude,
                                        location->latitude,
                                        s_location_global.latest_track_location.longitude,
                                        s_location_global.latest_track_location.latitude);  // 计算距离
    if (repo_get_login_state() != WLSDK_LOGIN_ONLINE) {
        WLLOGD("user offline");
        return;
    }
    if (distance < LOCATION_STILL_DISTANCE) {
        // 限制静止状态上报频率
        if (location->timestamp
            < s_location_global.latest_track_location.timestamp + LOCATION_REPORT_STILL_TIME_LIMIT) {
            WLLOGW("distance %f time %u latest timestamp %u",
                   distance,
                   location->timestamp,
                   s_location_global.latest_track_location.timestamp);
            return;
        }
    }
    location_info.userId       = repo_get_userid();
    location_info.clientType   = repo_get_extension_config_client_type();
    location_info.latitude     = location->latitude;
    location_info.longitude    = location->longitude;
    location_info.locationType = 0;
    location_info.speed        = location->speed;
    location_info.altitude     = location->altitude;
    location_info.radius       = location->radius;
    location_info.direction    = location->direction;
    location_info.timestamp    = location->timestamp;

    memcpy(&s_location_global.latest_track_location, location, sizeof(wlsdk_location));
    enc_result = weilapb_buildReportTrackLocationReq(&location_info);
    resultcode = protocol_client_send_request(&enc_result,
                                              wl_null,
                                              0,
                                              PROTOCOL_CLIENT_REQUEST_TIMEOUT_15,
                                              location_protocol_client_request_callback,
                                              wl_null);
    if (0 > resultcode) {
        WLLOGW("protocol_client_send_request error");
    }
}

static void location_report_geofence_alarm_check(wlsdk_location* location) {
    if (0 != repo_get_extension_config_location_interval()) {
        wl_list*         geofence_list = repo_get_extension_config_geofence_list();
        repo_geofence*   geofenceinfo  = wl_null;
        wl_uint32        timestamp     = repo_get_server_timestamp();
        wl_list_iterator iterator;
        geofenceinfo = (repo_geofence*)wl_list_get_first(geofence_list, &iterator);
        while (!wl_list_iterator_is_end(&iterator)) {
            // 控制告警频率最短时长
            if (timestamp > geofenceinfo->latest_report + HTTP_CLIENT_REQUEST_TIMEOUT_15) {
                wl_double distance = wl_getdistance(
                    location->longitude, location->latitude, geofenceinfo->longitude, geofenceinfo->latitude);
                // 在围栏内
                if (distance < geofenceinfo->radius) {
                    // 进入围栏, 上报围栏告警
                    if (geofenceinfo->transition_status != 1) {
                        location_report_geofence_alarm(location, geofenceinfo->id, 1);
                        geofenceinfo->latest_report = timestamp;
                    }
                }
                // 在围栏外
                else {
                    // 离开围栏,上报围栏告警
                    if (geofenceinfo->transition_status != 2) {
                        location_report_geofence_alarm(location, geofenceinfo->id, 2);
                        geofenceinfo->latest_report = timestamp;
                    }
                }
                if (timestamp > geofenceinfo->latest_report + 600) {
                    location_report_geofence_alarm(location, geofenceinfo->id, geofenceinfo->transition_status);
                    geofenceinfo->latest_report = timestamp;
                }
            }
            geofenceinfo = (repo_geofence*)wl_list_get_next(&iterator);
        }
    }
}

// static void location_report_business_location(wlsdk_location* location) {
//     PBEncResult    enc_result;
//     PBLocationInfo location_info;
//     wl_int32       resultcode;

//     if (repo_get_login_state() != WLSDK_LOGIN_ONLINE) {
//         WLLOGD("user offline");
//         return;
//     }
//     location_info.userId       = repo_get_userid();
//     location_info.clientType   = repo_get_extension_config_client_type();
//     location_info.locationType = 0;
//     location_info.latitude     = location->latitude;
//     location_info.longitude    = location->longitude;
//     location_info.speed        = location->speed;
//     location_info.altitude     = location->altitude;
//     location_info.radius       = location->radius;
//     location_info.direction    = location->direction;
//     location_info.timestamp    = location->timestamp;

//     enc_result = weilapb_buildReportBusinessLocationReq(&location_info, 5, 0);
//     resultcode = protocol_client_send_request(&enc_result,
//                                               wl_null,
//                                               0,
//                                               PROTOCOL_CLIENT_REQUEST_TIMEOUT_15,
//                                               location_protocol_client_request_callback,
//                                               wl_null);
//     if (0 > resultcode) {
//         WLLOGW("protocol_client_send_request error");
//     }
// }

static void process_set_yby_location(void* data, wl_int32 datalen) {
    wl_char        content[MAX_TEXT_LEN] = "";
    wl_char*       segment_begin         = data;
    wl_char*       segment_ptr           = wl_null;
    wl_char*       value_begin           = wl_null;
    wl_char*       value                 = wl_null;
    wl_bool        isgpsvalid            = wl_false;
    wl_char        date[MAX_NAME_LEN]    = {0};
    wlsdk_location location;
    wlsdk_lbs      lbs;
    wl_uint32      bts_index    = 0;
    wl_uint32      wifi_index   = 0;
    wl_uint32      mcc          = 0;
    wl_uint32      mnc          = 0;
    wl_uint32      segment_type = YBY_LOCATION_SEGMENT_TIMESTAMP_GPSSTATUS;
    wl_uint32      param_type   = 0;
    if (wl_null == data) {
        WLLOGW("data is null");
        return;
    }
    strncpy(content, data, sizeof(content) - 1);
    content[sizeof(content) - 1] = '\0';
    memset(&location, 0, sizeof(wlsdk_location));
    memset(&lbs, 0, sizeof(wlsdk_lbs));
    while (segment_begin != wl_null) {
        value_begin = segment_begin;
        segment_ptr = strchr(segment_begin, ';');
        if (segment_ptr) {
            *segment_ptr  = '\0';
            segment_begin = segment_ptr + 1;
        } else {
            segment_begin = wl_null;
        }
        param_type = 0;
        while (value_begin != wl_null) {
            value       = value_begin;
            segment_ptr = strchr(value_begin, ',');
            if (segment_ptr) {
                *segment_ptr = '\0';
                value_begin  = segment_ptr + 1;
            } else {
                value_begin = wl_null;
            }

            WLLOGT("value %s", value);
            if ('\0' == *value) {
                param_type++;
                continue;
            }
            switch (segment_type) {
                case YBY_LOCATION_SEGMENT_TIMESTAMP_GPSSTATUS:
                    // 时间戳
                    if (YBY_SEGMENT_TIMESTAMP == param_type) {
                        strncpy(date, value, sizeof(date) - 1);
                    } else if (YBY_SEGMENT_GPSSTATUS == param_type) {
                        if (0 == strcmp(value, "A")) {
                            isgpsvalid    = wl_true;
                            location.type = 0;
                        }
                    }
                    break;
                case YBY_LOCATION_SEGMENT_GPS_PARAMS:
                    if (isgpsvalid) {
                        if (YBY_SEGMENT_GPS_LATITUDE == param_type) {
                            location.latitude = atof(value);
                        } else if (YBY_SEGMENT_GPS_LATITUDE_LABEL == param_type) {
                            // 南纬取负
                            if (0 == strcmp(value, "S")) {
                                location.latitude = -location.latitude;
                            }
                        } else if (YBY_SEGMENT_GPS_LONGITUDE == param_type) {
                            location.longitude = atof(value);
                        } else if (YBY_SEGMENT_GPS_LONGITUDE_LABEL == param_type) {
                            // 西经取负
                            if (0 == strcmp(value, "W")) {
                                location.longitude = -location.longitude;
                            }
                        } else if (YBY_SEGMENT_GPS_SPEED == param_type) {
                            location.speed = atof(value);
                        } else if (YBY_SEGMENT_GPS_DIRECTION == param_type) {
                            location.direction = atof(value);
                        } else if (YBY_SEGMENT_GPS_ALTITUDE == param_type) {
                            location.altitude = atof(value);
                        } else if (YBY_SEGMENT_GPS_SATELLITE_COUNT == param_type) {
                        } else if (YBY_SEGMENT_GPS_SIGNAL == param_type) {
                        }
                    }
                    break;
                case YBY_LOCATION_SEGMENT_CDMA_PARAMS:
                    if (YBY_SEGMENT_CDMA_STATUS == param_type) {
                        lbs.bts.iscdma = atoi(value);
                    } else if (YBY_SEGMENT_CDMA_SID == param_type) {
                        lbs.bts.bts.cdma.sid = atoi(value);
                    } else if (YBY_SEGMENT_CDMA_NID == param_type) {
                        lbs.bts.bts.cdma.nid = atoi(value);
                    } else if (YBY_SEGMENT_CDMA_BID == param_type) {
                        lbs.bts.bts.cdma.bid = atoi(value);
                    } else if (YBY_SEGMENT_CDMA_LON == param_type) {
                    } else if (YBY_SEGMENT_CDMA_LAT == param_type) {
                    } else if (YBY_SEGMENT_CDMA_SIGNAL == param_type) {
                        lbs.bts.bts.cdma.signal = atoi(value);
                    }
                    break;
                case YBY_LOCATION_SEGMENT_BTS_PARAMS:
                    if (bts_index < MAX_NEAR_BTS_NUM) {
                        if (YBY_SEGMENT_BTS_COUNT == param_type) {
                        } else if (YBY_SEGMENT_BTS_CONNECT_COUNT == param_type) {
                        } else if (YBY_SEGMENT_BTS_MCC == param_type) {
                            mcc = atoi(value);
                        } else if (YBY_SEGMENT_BTS_MNC == param_type) {
                            mnc = atoi(value);
                        } else if (YBY_SEGMENT_BTS_LAC == param_type) {
                            // 第一个基站默认为连接基站
                            if (0 == bts_index) {
                                lbs.bts.bts.mobile.mcc = mcc;
                                lbs.bts.bts.mobile.mnc = mnc;
                                lbs.bts.bts.mobile.lac = atoi(value);
                            } else {
                                lbs.nearbts_count++;
                                lbs.nearbts[bts_index - 1].bts.mobile.mcc = mcc;
                                lbs.nearbts[bts_index - 1].bts.mobile.mnc = mnc;
                                lbs.nearbts[bts_index - 1].bts.mobile.lac = atoi(value);
                            }
                        } else if (YBY_SEGMENT_BTS_CELLID == param_type) {
                            // 第一个基站默认为连接基站
                            if (0 == bts_index) {
                                lbs.bts.bts.mobile.cellid = atoi(value);
                            } else {
                                lbs.nearbts[bts_index - 1].bts.mobile.cellid = atoi(value);
                            }

                        } else if (YBY_SEGMENT_BTS_SIGNAL == param_type) {
                            // 第一个基站默认为连接基站
                            if (0 == bts_index) {
                                lbs.bts.bts.mobile.signal = atoi(value);
                            } else {
                                lbs.nearbts[bts_index - 1].bts.mobile.signal = atoi(value);
                            }
                            // 附近基站重新定位
                            param_type = YBY_SEGMENT_BTS_MNC;
                            bts_index++;
                        }
                    }
                    break;
                case YBY_LOCATION_SEGMENT_WIFI_PARAMS:
                    if (wifi_index < MAX_NEAR_WIFI_NUM) {
                        if (YBY_SEGMENT_WIFI_COUNT == param_type) {
                            lbs.nearwifi_count = atoi(value);
                        } else if (YBY_SEGMENT_WIFI_NAME == param_type) {
                            strncpy(lbs.nearwifi[wifi_index].ssid, value, sizeof(lbs.nearwifi[wifi_index].ssid) - 1);
                        } else if (YBY_SEGMENT_WIFI_MAC == param_type) {
                            strncpy(lbs.nearwifi[wifi_index].mac, value, sizeof(lbs.nearwifi[wifi_index].mac) - 1);
                        } else if (YBY_SEGMENT_WIFI_SIGNAL == param_type) {
                            lbs.nearwifi[wifi_index].signal = atoi(value);
                            // 附近WIFI重新定位
                            param_type = YBY_SEGMENT_WIFI_COUNT;
                            wifi_index++;
                        }
                    }
                    break;
                default:
                    break;
            }
            param_type++;
        }
        segment_type++;
    }
    if (isgpsvalid) {
        location_set_location msgdata;
        memset(&msgdata, 0, sizeof(location_set_location));
        memcpy(&msgdata.location, &location, sizeof(wlsdk_location));
        msgdata.isgps = wl_true;
        strncpy(msgdata.date, date, sizeof(date) - 1);
        strncpy(msgdata.content, content, sizeof(msgdata.content) - 1);
        msgdata.content[sizeof(msgdata.content) - 1] = '\0';
        process_set_location(&msgdata, sizeof(msgdata));
    } else {
        location_set_lbs msgdata;
        memset(&msgdata, 0, sizeof(location_set_lbs));
        memcpy(&msgdata.lbs, &lbs, sizeof(wlsdk_lbs));
        strncpy(msgdata.content, content, sizeof(msgdata.content) - 1);
        msgdata.content[sizeof(msgdata.content) - 1] = '\0';
        process_set_lbs(&msgdata, sizeof(msgdata));
    }
}

static void process_set_location(void* data, wl_int32 datalen) {
    location_set_location* msgdata      = (location_set_location*)data;
    wl_time                current_time = repo_get_server_timestamp();
    // GPS位置优先级最高
    if (msgdata->isgps) {
        memcpy(&s_location_global.latest_gps, &msgdata->location, sizeof(wlsdk_location));
        s_location_global.latest_gps.timestamp = current_time;
        memcpy(&s_location_global.latest_location, &s_location_global.latest_gps, sizeof(wlsdk_location));
    } else {
        // GPS位置优先LBS
        if (current_time < s_location_global.latest_gps.timestamp + LOCATION_GPS_PRORITY_TIME) {
            memcpy(&s_location_global.latest_location, &s_location_global.latest_gps, sizeof(wlsdk_location));
            s_location_global.latest_location.timestamp = current_time;
        } else {
            memcpy(&s_location_global.latest_location, &msgdata->location, sizeof(wlsdk_location));
        }
    }

    if (repo_get_login_state() != WLSDK_LOGIN_ONLINE) {
        WLLOGD("user offline");
        return;
    }
    // 实时位置共享
    if (s_location_global.is_share_report) {
        if (wl_list_size(&s_location_global.share_list) > 0) {
            location_report_location(&s_location_global.latest_location);
        }
        s_location_global.is_share_report = wl_false;
    }
    // 轨迹上报
    if (s_location_global.is_track_report) {
        location_report_track_location(&s_location_global.latest_location);
        s_location_global.is_track_report = wl_false;
    }
    // 电子围栏检查
    location_report_geofence_alarm_check(&s_location_global.latest_location);
    // SOS上报
    if (s_location_global.is_sos_report) {
        location_report_sos_gps_location(&s_location_global.latest_location);
        s_location_global.is_sos_report = wl_false;
    }
}

static void process_get_lbs(void* data, wl_int32 datalen) {
    location_get_lbs* msgdata = (location_get_lbs*)data;

    if (repo_get_login_state() != WLSDK_LOGIN_ONLINE) {
        WLLOGD("user offline");
        return;
    }

    location_get_lbs_location(&msgdata->lbs);
}

static void process_set_lbs(void* data, wl_int32 datalen) {
    location_set_lbs* msgdata      = (location_set_lbs*)data;
    wl_time           current_time = repo_get_server_timestamp();

    if (repo_get_login_state() != WLSDK_LOGIN_ONLINE) {
        WLLOGD("user offline");
        return;
    }
    // SOS直接上报LBS
    if (s_location_global.is_sos_report) {
        location_report_sos_lbs_location(&msgdata->lbs, msgdata->content);
        s_location_global.is_sos_report = wl_false;
    }
    // 位置有效期内,不用更新LBS位置
    if (current_time < s_location_global.latest_location.timestamp + LOCATION_VALID_TIME) {
        // 实时位置共享
        if (s_location_global.is_share_report) {
            if (wl_list_size(&s_location_global.share_list) > 0) {
                location_report_location(&s_location_global.latest_location);
            }
            s_location_global.is_share_report = wl_false;
        }
        // 轨迹上报
        if (s_location_global.is_track_report) {
            location_report_track_location(&s_location_global.latest_location);
            s_location_global.is_track_report = wl_false;
        }
        // 电子围栏检查
        location_report_geofence_alarm_check(&s_location_global.latest_location);
    } else {
        location_get_lbs_location(&msgdata->lbs);
    }
}

static wl_int32 location_message_callback(wl_int32 msgid, void* data, wl_uint32 datalen) {
    WLLOGD("msgid %d", msgid);
    switch (msgid) {
        case LOCATION_MSG_SET_YBY_LOCATION:
            process_set_yby_location(data, datalen);
            break;
        case LOCATION_MSG_SET_LOCATION:
            process_set_location(data, datalen);
            break;
        case LOCATION_MSG_SET_LBS:
            process_set_lbs(data, datalen);
            break;
        case LOCATION_MSG_GET_LBS:
            process_get_lbs(data, datalen);
            break;
        // 登录成功,是否需要上报轨迹
        case LOCATION_MSG_REFRESH_TRACK_CONFIG: {
            location_track_config_refresh();
            break;
        }
        case LOCATION_MSG_OPEN_SOS: {
            // 开启了新sos,生成新的sn(防止按键重复按压触发多次)
            if (!s_location_global.is_sos_report) {
                s_location_global.sos_sn = repo_get_server_timestamp();
            }
            s_location_global.is_sos_report              = wl_true;
            s_location_global.sos_phone_left_retry_times = LOCATION_SOS_RETRY_MAX_TIMES;
            s_location_global.sos_weila_left_retry_times = LOCATION_SOS_RETRY_MAX_TIMES;
            location_sdk_message_callback(WLSDK_MSG_LOCATION_REQUEST_SOS, WLSDK_RESULT_SUCCESS, wl_null, 0);

            // 启动SOS重试定时器
            if (WL_INVALID_TIMER != s_location_global.sos_timer) {
                wl_timer_stop(s_location_global.sos_timer);
                s_location_global.sos_timer = WL_INVALID_TIMER;
            }
            // 超时定时器
            s_location_global.sos_timer = wl_timer_start(
                LOCATION_SOS_TIMER_INTERVAL, REPETITIVE, location_sos_timer_callback, wl_null, WL_TASK_APP, "loc_sos");
            WLLOGI("timer %p", s_location_global.sos_timer);
            if (WL_INVALID_TIMER == s_location_global.sos_timer) {
                WLLOGW("wl_timer_start fail");
                return 0;
            }
            break;
        }
        case LOCATION_MSG_CLOSE_SOS: {
            s_location_global.is_sos_report              = wl_false;
            s_location_global.sos_sn                     = 0;
            s_location_global.sos_phone_left_retry_times = 0;
            s_location_global.sos_weila_left_retry_times = 0;
            if (WL_INVALID_TIMER != s_location_global.sos_timer) {
                wl_timer_stop(s_location_global.sos_timer);
                s_location_global.sos_timer = WL_INVALID_TIMER;
            }
            break;
        }
        default:
            WLLOGW("msgid %d not support", msgid);
            break;
    }
    return 0;
}

static void process_report_location_message(PBWeilaMsg* message, void* content, wl_uint32 contentlen, void* context) {
    WLLOGI("resultCode %d", message->resultCode);
    if (WLSDK_RESULT_SUCCESS == message->resultCode) {
    } else {
    }
}

static void
    process_report_track_location_message(PBWeilaMsg* message, void* content, wl_uint32 contentlen, void* context) {
    WLLOGI("resultCode %d", message->resultCode);
    if (WLSDK_RESULT_SUCCESS == message->resultCode) {
    } else {
    }
}

static void
    process_report_business_location_message(PBWeilaMsg* message, void* content, wl_uint32 contentlen, void* context) {
    WLLOGI("resultCode %d", message->resultCode);
    if (WLSDK_RESULT_SUCCESS == message->resultCode) {
    } else {
    }
}

static void process_location_control_notify_message(PBWeilaMsg* message, void* context) {
    PBLocationCtrlNtf* notify = (PBLocationCtrlNtf*)message->pContent;
    // 开启位置共享
    if (1 == notify->status) {
        location_start_share(notify->sessionId, notify->sessionType, notify->duration, notify->frequency);
    }
    // 关闭位置共享
    else {
        location_stop_share(notify->sessionId, notify->sessionType);
    }
}

static wl_int32
    location_protocol_client_request_callback(PBWeilaMsg* message, void* content, wl_uint32 contentlen, void* context) {
    WLLOGI("message %d resultCode %d", message->message, message->resultCode);
    switch (message->message) {
        case WL_LOCATION_REPORT_LOCATION_MSG:
            process_report_location_message(message, content, contentlen, context);
            break;
        case WL_LOCATION_REPORT_TRACK_LOCATION_MSG:
            process_report_track_location_message(message, content, contentlen, context);
            break;
        case WL_LOCATION_REPORT_BUSINESS_LOCATION_MSG:
            process_report_business_location_message(message, content, contentlen, context);
            break;
        default:
            break;
    }
    return 0;
}

static wl_int32 location_protocol_client_listen_callback(PBWeilaMsg* message, void* context) {
    WLLOGI("message %d", message->message);
    switch (message->message) {
        case WL_LOCATION_LOCATION_CONTROL_MSG: {
            process_location_control_notify_message(message, context);
            break;
        }
        default:
            break;
    }
    return 0;
}

#ifdef __cplusplus
}
#endif
